/*
 * @Author: dark-snow 1306808708@qq.com
 * @Date: 2023-07-04 16:57:21
 * @LastEditors: dark-snow 1306808708@qq.com
 * @LastEditTime: 2023-07-06 10:25:31
 * @FilePath: \wifi_server_030704\components\user_component\user_socket.c
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include <stdio.h>
#include "user_socket.h"

#define MAX_CLIENT_CONNECT_NUM 5
#define HOST_PORT 8080

const static char *TAG = "USER WIFI";
SemaphoreHandle_t Sem_Client_Connect_Num;
// dict *socket_dict_head;
int listen_sock;

// #include "esp_wifi.h"

void tcp_server_init_task(void *pt)
{
	char addr_str[128];
	int addr_family;
	int ip_protocol;

	ESP_LOGI(TAG, "tcp_server_init_task");

	Sem_Client_Connect_Num = xSemaphoreCreateCounting(MAX_CLIENT_CONNECT_NUM + 1, MAX_CLIENT_CONNECT_NUM);

	addr_family = AF_INET;
	ip_protocol = IPPROTO_IP;

	struct sockaddr_in destAddr;
	destAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	destAddr.sin_family = AF_INET;
	destAddr.sin_port = htons(HOST_PORT);
	inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1);
	ESP_LOGI(TAG, "addr_str:%s", addr_str);

	// 初始化
	listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
	if (listen_sock < 0)
	{
		ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
		vTaskDelete(NULL);
		// break;
	}
	int opt = 1;
	//设置套接字属性，使用该函数可以解决无法重新正确bind的问题
	setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	ESP_LOGI(TAG, "Socket created");

	// 绑定
	int err = bind(listen_sock, (struct sockaddr *)&destAddr, sizeof(destAddr));
	if (err != 0)
	{
		ESP_LOGE(TAG, "Socket unable to bind: err %d", err);
		ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
		vTaskDelete(NULL);
		// break;
	}
	ESP_LOGI(TAG, "Socket binded");

	// 监听
	err = listen(listen_sock, 2); // 连接等待队列数量，并非连入客户端数量
	if (err != 0)
	{
		ESP_LOGE(TAG, "Error occured during listen: errno %d", errno);
		vTaskDelete(NULL);
		// break;
	}
	ESP_LOGI(TAG, "Socket listening");

	// client_dict_init_head(socket_dict_head, &listen_sock, NULL);
	//  创建监听函数
	xTaskCreate(tcp_server_accpt_mult_task, "tcp accept", 4096, &listen_sock, 2, NULL);

	vTaskDelete(NULL);
}

void tcp_server_accpt_mult_task(void *pt)
{
	// 等待连入
	Client_Info client_info;
	struct sockaddr_in addr;

	uint addrLen = sizeof(addr);
	int listen_sock = *((int *)pt);

	while (1) // 循环保持监听
	{
		xSemaphoreTake(Sem_Client_Connect_Num, portMAX_DELAY);
		int sock = accept(listen_sock, (struct sockaddr *)&addr, &addrLen);
		if (sock < 0)
		{
			xSemaphoreGive(Sem_Client_Connect_Num);
			ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
			break;
		}
		ESP_LOGI(TAG, "Socket accepted");

		// 临时用于传递信息
		client_info.sock = sock;
		client_info.addr = addr;
		client_info.addrLen = addrLen;

		// 没写好
		// client_dict_insert_node(socket_dict_head, &sock, &client_info);
		client_list_add(client_info);

		// 监听到客户端连入，开启多任务（线程）进行数据接受
		// 可以考虑将client_sock、sourceAddr信息写入字典（类似）进行信息保存
		// 也可将client_sock,sourceAddr信息整合为结构体------目前使用 √
		xTaskCreate(recv_clientSock_info_task, "sock_recv", 4096, &client_info, 3, NULL);
	}
	vTaskDelete(NULL);
}

void recv_clientSock_info_task(Client_Info *client_info)
{
	// 临时用于传递信息
	char addr_str[128];
	int sock = client_info->sock;
	struct sockaddr_in addr = client_info->addr;
	// uint addrLen = client_info->addrLen;

	// 无法知道是哪个具体的ip发送过来的信息
	// 需要增加srouceAddr结构体入参才可完成该功能
	char rx_buffer[128];
	// 接收信息
	while (1)
	{
		int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
		// Error occured during receiving
		if (len < 0)
		{
			ESP_LOGE(TAG, "recv failed: errno %d", errno);
			break;
		}
		// Connection closed
		else if (len == 0)
		{
			ESP_LOGI(TAG, "Connection closed");
			break;
		}
		// Data received
		else
		{
			inet_ntoa_r(((struct sockaddr_in *)&addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);

			rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
			ESP_LOGI(TAG, "Received %d bytes from %s(%d):%s", len, addr_str, addr.sin_port, rx_buffer);

			int err = send(sock, rx_buffer, len, 0);
			if (err < 0)
			{
				ESP_LOGE(TAG, "Error occured during sending: errno %d", errno);
				break;
			}
		}
	}

	// if (sock != -1) // 客户端断开链接，关闭客户端资源
	{
		tcp_client_close(sock);
	}
	client_list_remove(sock);
	// client_dict_delete_node(socket_dict_head, sock);
	xSemaphoreGive(Sem_Client_Connect_Num);
	vTaskDelete(NULL);
}

// 关闭服务器时，客户端无法正常关闭
void tcp_server_close(void *pt)
{
	ESP_LOGI(TAG, "listen sock close start");

	for (int i = 0; i < SOCK_MAX_NUM; i++)
	{
		if (client_list[i].sock < 1)
			continue;

		shutdown(client_list[i].sock, 0);
		close(client_list[i].sock);
		ESP_LOGW(TAG, "socket client closing");
		vTaskDelay(50 / portTICK_PERIOD_MS);
	}

	shutdown(listen_sock, 0);
	close(listen_sock);
	ESP_LOGW(TAG, "socket server closing");
	// listen_sock = -1;
	vSemaphoreDelete(Sem_Client_Connect_Num);

	client_list_remove_all();

	vTaskDelay(500 / portTICK_PERIOD_MS);
	// client_dict_clear_node(socket_dict_head);
	// vTaskDelete(NULL);
}

void tcp_client_close(int sock)
{
	// ESP_LOGE(TAG, "client sock = %d", sock);
	ESP_LOGW(TAG, "Shutting down client socket");
	shutdown(sock, 0);
	close(sock);
}

void tcp_send(int sock, char *tx_buf)
{
	int err = send(sock, tx_buf, strlen(tx_buf), 0);
	if (err < 0)
		ESP_LOGE(TAG, "Error occured during sending: errno %d", errno);
}

/******************        client list                 ******************/
// 通过数组来代替字典功能，降低开发难度
// #define SOCK_MAX_NUM (MAX_CLIENT_CONNECT_NUM + 1)

const static char *listTAG = "client dict";
Client_Info client_list[SOCK_MAX_NUM];

void client_list_add(Client_Info data)
{
	for (int i = 0; i < SOCK_MAX_NUM; i++)
	{
		if (client_list[i].sock < 1)
		{
			client_list[i].sock = data.sock;
			client_list[i].addr = data.addr;
			client_list[i].addrLen = data.addrLen;
			ESP_LOGI(listTAG, "client list add complish");
			return;
		}
	}
	ESP_LOGW(listTAG, "client list add fail");
}

void client_list_remove(int sock)
{
	for (int i = 0; i < SOCK_MAX_NUM; i++)
	{
		if (client_list[i].sock == sock)
		{
			ESP_LOGI(listTAG, "client list remove %d successful", sock);
			client_list[i].sock = 0;
			return;
		}
		else if (client_list[i].sock < 1)
		{
			ESP_LOGW(listTAG, "client list remove %d not find target", sock);
			return;
		}
	}

	ESP_LOGW(listTAG, "client list remove expected error");
}

void client_list_remove_all(void)
{
	for (int i = 0; i < SOCK_MAX_NUM; i++)
	{
		if (client_list[i].sock > 0)
		{
			ESP_LOGI(listTAG, "client list remove %d successful", client_list[i].sock);
			client_list[i].sock = 0;
		}
		else if (client_list[i].sock < 1)
			continue;
	}
	ESP_LOGI(listTAG, "client list remove all successful");
}

// 这些函数的引用导致了核心恐慌，需要改进
// 改进难度过大，之后再说
/******************        client dict                 ******************/

// const static char *clientTAG = "client dict";

// /**
//  * @description: 根据key(sock)，读取out_data
//  * @param {dict} *head
//  * @param {int} key
//  * @return {Client_Info *}客户端数据
//  * @use:
//  */
// Client_Info *client_dict_read_data(dict *head, int key)
// {
// 	dict *node = head;
// 	Client_Info *out_data;
// 	do
// 	{
// 		if (key == client_dict_read_sock(node))
// 		{
// 			ESP_LOGI(clientTAG, "read data triumph");
// 			out_data = (Client_Info *)node->data;
// 			return out_data;
// 		}
// 		node = node->next;
// 	} while (node->next != NULL);
// 	ESP_LOGI(clientTAG, "read data failure");
// 	return NULL;
// }

// void client_dict_init_head(dict *head, int *key, Client_Info *data)
// {
// 	dict_init_head(head, (void *)key, (void *)data);
// }
// void client_dict_insert_node(dict *head, int *key, Client_Info *data)
// {
// 	dict_insert_node(head, (void *)key, (void *)data);
// }
// void client_dict_delete_node(dict *head, int key)
// {
// 	int num = 0;
// 	dict *node = head;
// 	do
// 	{
// 		if (key == client_dict_read_sock(node))
// 		{
// 			dict_delete_node(head, num);
// 			ESP_LOGI(clientTAG, "delete node triumph");
// 		}

// 		num++;
// 		node = node->next;
// 	} while (node->next != NULL);
// 	ESP_LOGI(clientTAG, "delete node failure");
// }
// void client_dict_clear_node(dict *head)
// {
// 	dict_clear_node(head);
// }
